
#include "bsp.h"
#include "bsp_delay.h"
#include "includes.h"
#include "libs/mimxrt1052/fsl_dmamux.h"
#include "libs/mimxrt1052/fsl_iomuxc.h"
#include "libs/mimxrt1052/fsl_lpuart.h"
#include "libs/mimxrt1052/fsl_lpuart_edma.h"

#define BSP_DEBUG_LOCAL_BUF_SIZE  (1024)
#define BSP_DEBUG_TX_EDMA_CHANNEL (0U)

static uint8_t m_local_buf[2][BSP_DEBUG_LOCAL_BUF_SIZE];
static uint8_t m_local_pt = 0;
static uint32_t m_local_size[2];
static lpuart_edma_handle_t m_lpuart_edma_handle;
static edma_handle_t m_edma_handle;
static bool m_is_edma_busy = false;

int bsp_debug_stream(const char *buf, int len) {
#if 0
  LPUART_WriteBlocking(LPUART1, (const uint8_t*)buf, len);
#else
  uint32_t mask = 0;
  mask          = DisableGlobalIRQ();
  if (m_local_size[m_local_pt] < BSP_DEBUG_LOCAL_BUF_SIZE) {
    // buffer not full
    // copy into the buf
    int cplen = MIN(len, BSP_DEBUG_LOCAL_BUF_SIZE - m_local_size[m_local_pt]);
    memcpy(m_local_buf[m_local_pt] + m_local_size[m_local_pt], buf, cplen);
    m_local_size[m_local_pt] += cplen;
    // if the dma is not running, startup the dma
    if (!m_is_edma_busy) {
      // over or error
      lpuart_transfer_t xfer;
      xfer.data                = m_local_buf[m_local_pt];
      xfer.dataSize            = m_local_size[m_local_pt];
      m_local_size[m_local_pt] = 0;
      LPUART_SendEDMA(LPUART1, &m_lpuart_edma_handle, &xfer);
      m_is_edma_busy = true;
      m_local_pt     = !m_local_pt;
    }
  }
  EnableGlobalIRQ(mask);
#endif

  return 0;
}

static int bsp_debug_serial_io_init() { return 0; }

static void bsp_debug_uart_callback(LPUART_Type *base, lpuart_edma_handle_t *handle, status_t status, void *para);
static int bsp_debug_serial_init() {
  lpuart_config_t config;

  uint32_t freq;
  if (CLOCK_GetMux(kCLOCK_UartMux) == 0) {
    // PLL3 div6
    freq = CLOCK_GetPllFreq(kCLOCK_PllUsb1) / 6 / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1);
  } else {
    // bypass
    freq = CLOCK_GetOscFreq() / (CLOCK_GetDiv(kCLOCK_UartDiv) + 1);
  }

  LPUART_GetDefaultConfig(&config);
  config.baudRate_Bps = 115200;
  config.enableTx     = true;
  config.enableRx     = false;
  LPUART_Init(LPUART1, &config, freq);

  /* DMA part */
  LPUART_TransferCreateHandleEDMA(LPUART1, &m_lpuart_edma_handle, bsp_debug_uart_callback, NULL, &m_edma_handle, NULL);
  return 0;
}

static void bsp_debug_uart_callback(LPUART_Type *base, lpuart_edma_handle_t *handle, status_t status, void *para) {
  if (kStatus_LPUART_TxIdle == status) {
    // recheck
    if (m_local_size[m_local_pt] == 0x00) {
      m_is_edma_busy = false;
      return;
    }
    // over or error
    lpuart_transfer_t xfer;
    xfer.data                = m_local_buf[m_local_pt];
    xfer.dataSize            = m_local_size[m_local_pt];
    m_local_size[m_local_pt] = 0;
    LPUART_SendEDMA(LPUART1, &m_lpuart_edma_handle, &xfer);
    m_local_pt = !m_local_pt;
  }
}
static int bsp_debug_dma_init() {
  edma_config_t config;

#if defined(FSL_FEATURE_SOC_DMAMUX_COUNT) && FSL_FEATURE_SOC_DMAMUX_COUNT
  /* Init DMAMUX */
  DMAMUX_Init(DMAMUX);
  /* Set channel for LPUART */
  DMAMUX_SetSource(DMAMUX, 0U, kDmaRequestMuxLPUART1Tx);
  DMAMUX_EnableChannel(DMAMUX, 0U);
#endif
  EDMA_GetDefaultConfig(&config);
  EDMA_Init(DMA0, &config);
  EDMA_CreateHandle(&m_edma_handle, DMA0, 0U);
  return 0;
}

int bsp_debug_init() {
  bsp_debug_serial_io_init();
  bsp_debug_dma_init();
  bsp_debug_serial_init();

#if 0
  while (1) {
    bsp_debug_stream("hello\r\n", 7);
    //bsp_delay_ms(5);
  }
#endif
  return 0;
}
